-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local def = require 'biz_topo.def'
local mdb = require 'mc.mdb' 
local mdb_service = require 'mc.mdb.mdb_service'
local class_mgnt = require 'mc.class_mgnt'
local object_manage = require 'mc.mdb.object_manage'
local class = require 'mc.class' 
local log = require 'mc.logging'

local DEFAULT_INTERFACE<const> = 'bmc.kepler.Systems.PCIeDevice.PCIeFunction'
local UINT8_MAX<const> = 0xff
---@class PCIeFunction @PCIe设备
---@field public mds table @MDS对象
local c_pcie_function = class()

function c_pcie_function:get_prop(name, interface)
    if not self.mds or not name then
        return nil
    end

    if interface and self.mds[interface] then
        return self.mds[interface][name]
    elseif self.mds[name] then
        return self.mds[name]
    elseif self.mds[DEFAULT_INTERFACE] then
        return self.mds[DEFAULT_INTERFACE][name]
    end

    return nil
end

function c_pcie_function:set_prop(name, val, interface)
    if not self.mds or not name then
        return nil
    end

    if interface and self.mds[interface] and self.mds[interface][name] ~= nil then
        self.mds[interface][name] = val
    elseif self.mds[name] then
        self.mds[name] = val
    elseif self.mds[DEFAULT_INTERFACE] and self.mds[DEFAULT_INTERFACE][name] ~= nil then
        self.mds[DEFAULT_INTERFACE][name] = val
    end

    return nil
end

-- uptree时检索是否已经有对象，如果有则原地修改，否则上树
-- vdss = {vid, did, sid, svid}
function c_pcie_function:uptree(pcie_device, vdss)
    -- 从资源树获取PCIeAddrInfo
    log:notice('[PCIeFunction] Uptree PCIeFunction, BDF = %s:%s.%s', self.bus_number, self.device_number, self.function_number)
    local filter = "{ 'SlotID': " .. self.slot_id .. "," .. 
        "'ComponentType': " .. pcie_device.mds.DeviceType .. "," ..
        "'SocketID': " .. self.record.LogicProcessorId .. "}"
    local pcie_addr_info_path = mdb_service.get_path(self.bus, 'bmc.kepler.Systems.PcieAddrInfo', filter, true)
    local pcie_addr_info_obj
    if pcie_addr_info_path and pcie_addr_info_path.Path and pcie_addr_info_path.Path ~= '' then
        pcie_addr_info_obj = mdb.get_object(self.bus, pcie_addr_info_path.Path, 'bmc.kepler.Systems.PcieAddrInfo')
    end

    local name = "Function" .. "_" .. def.type_to_com_type[self.device_type] .. "_" .. self.slot_id .. "_" ..
        self.bus_number .. "_" .. self.device_number .. "_" .. self.function_number
    pcall(function()
        local pcie_func_class_mgnt = class_mgnt('PCIeFunction')
        self.mds = pcie_func_class_mgnt[name]
    end)

    local function_protocol = pcie_device.mds.Function
    local funciton_type = pcie_device.mds.FunctionType

    if function_protocol ~= "CXL" and funciton_type ~= "PCIe" then
        function_protocol = "PCIe"
    end

    if funciton_type ~= "Physical" and funciton_type ~= "Virtual" then
        funciton_type = "Physical"
    end

    if self.mds then
        self.mds.SegmentNumber = self.record.SegmentNumber
        self.mds.BusNumber = self.bus_number
        self.mds.DeviceNumber = self.device_number
        self.mds.FunctionNumber = self.function_number
        self.mds.RelatedProcessorId = self.record.LogicProcessorId
        self.mds.RootBusNumber = pcie_addr_info_obj and pcie_addr_info_obj.Bus or 0xff
        self.mds.RootDeviceNumber = pcie_addr_info_obj and pcie_addr_info_obj.Device or 0xff
        self.mds.RootFunctionNumber = pcie_addr_info_obj and pcie_addr_info_obj.Function or 0xff
        self.mds.VendorId = vdss[1]
        self.mds.DeviceId = vdss[2]
        self.mds.SubsystemId = vdss[3]
        self.mds.SubsystemVendorId = vdss[4]
        self.mds.FunctionProtocol = function_protocol
        self.mds.FunctionType = funciton_type
        self.mds.BaseClassCode = pcie_device.mds.BaseClassCode
        self.mds.SubClassCode = pcie_device.mds.SubClassCode
        self.mds.ProgrammingInterface = pcie_device.mds.ProgrammingInterface
        self.mds.DeviceType = self.device_type
        self.mds.SlotId = self.slot_id
        self.mds.BDF = self.bus_number .. ":" .. self.device_number .. "." .. self.function_number
        self.mds:register_mdb_objects()
    else
        local path = '/bmc/kepler/Systems/' .. self.record.SystemId .. '/PCIeDevices/' .. pcie_device:get_prop('ObjectName') .. '/PCIeFunctions/' .. name
        self.mds = object_manage.create_object('PCIeFunction', name, path, function(obj)
            obj.ObjectName = name
            obj.SegmentNumber = self.record.SegmentNumber
            obj.BusNumber = self.bus_number
            obj.DeviceNumber = self.device_number
            obj.FunctionNumber = self.function_number
            obj.RelatedProcessorId = self.record.LogicProcessorId
            obj.RootBusNumber = pcie_addr_info_obj and pcie_addr_info_obj.Bus or 0xff
            obj.RootDeviceNumber = pcie_addr_info_obj and pcie_addr_info_obj.Device or 0xff
            obj.RootFunctionNumber = pcie_addr_info_obj and pcie_addr_info_obj.Function or 0xff
            obj.VendorId = vdss[1]
            obj.DeviceId = vdss[2]
            obj.SubsystemId = vdss[3]
            obj.SubsystemVendorId = vdss[4]
            obj.FunctionProtocol = function_protocol
            obj.FunctionType = funciton_type
            obj.BaseClassCode = pcie_device.mds.BaseClassCode
            obj.SubClassCode = pcie_device.mds.SubClassCode
            obj.ProgrammingInterface = pcie_device.mds.ProgrammingInterface
            obj.DeviceType = self.device_type
            obj.SlotId = self.slot_id
            obj.BDF = self.bus_number .. ":" .. self.device_number .. "." .. self.function_number
        end)
    end
end

function c_pcie_function:update_info(device_type, sys_id, card_info)
    self.record.DeviceType = device_type
    self.record.SlotId = card_info.SlotId
    self.record.SegmentNumber = card_info.SegmentNumber
    self.record.LogicProcessorId = card_info.LogicProcessorId
    self.record.BusNumber = card_info.BusNumber
    self.record.DeviceNumber = card_info.DeviceNumber
    self.record.FunctionNumber = card_info.FunctionNumber
    self.record.SystemId = sys_id
    self.record:save()
end

function c_pcie_function:delete()
    self.record:delete()
    if self.mds then
        local pcie_func_class_mgnt = class_mgnt('PCIeFunction')
        pcie_func_class_mgnt:remove(self.mds)
        self.mds = nil
    end
end

function c_pcie_function:downtree()
    if self.mds then
        self.mds:unregister_mdb_objects()
    end
end

-- 创建时匹配框架资源树对象，如果有则使用
function c_pcie_function:init()
    local name = "Function" .. "_" .. def.type_to_com_type[self.device_type] .. "_" .. self.slot_id .. "_" ..
        self.bus_number .. "_" .. self.device_number .. "_" .. self.function_number
    log:notice('[PCIeFunction] Create a PCIeFunction, name = %s', name)
    print("pre load")
    pcall(function()
        local pcie_func_class_mgnt = class_mgnt('PCIeFunction')
        self.mds = pcie_func_class_mgnt[name]
    end)
end

function c_pcie_function:ctor(bus, record)
    self.bus = bus
    self.record = record
    self.device_type = record.DeviceType
    self.bus_number = record.BusNumber
    self.device_number = record.DeviceNumber
    self.function_number = record.FunctionNumber
    self.slot_id = record.SlotId 
end

return c_pcie_function
